import random
from datetime import datetime, timedelta

from osc02.domain.uncertainties.uncertainties import AbsUncertainty
from osc02.domain.uncertainties.weatherUncertaintyUtil import get_sampled_precipitation_times_in_period
from osc02.static.constants import Commons, Constants


# Time unit is hour
class PrecipitationUncertainty(AbsUncertainty):
    def __init__(self, env, seed=1, name="PRECIPITATION",
                 start_date=datetime(2023, 3, 10),
                 end_date=datetime(2023, 7, 30),
                 time_to_resume_from_precipitation=1):
        self.env = env
        self.name = name
        self.start_date = start_date
        self.end_date = end_date
        self.seed = seed
        self.sampled_precipitation_times_dict = None
        self.time_to_resume_from_precipitation = time_to_resume_from_precipitation
        # get sampled_precipitation_times in the project duration
        self.time_between_failure_stack = self.sample_time_between_failure()
        self.current_date_str = None
        # start the break process
        env.process(self.precipitation_interrupt_erection())

    def get_event_source_name(self):
        return self.name + "/" + self.get_current_date_str()

    def get_time_to_resume(self):
        return self.time_to_resume_from_precipitation

    def get_current_date_str(self):
        return self.current_date_str

    def get_time_to_next_precipitation(self):
        # try to produce one interrupt every day
        return Constants.WORKING_HOURS

    # to simulate time passes before interruption occurs
    def get_time_precipitation_occur_after_erection_start(self):
        random.seed(self.seed)
        return random.randint(0, Constants.WORKING_HOURS)

    def sample_time_between_failure(self):
        random.seed(self.seed)
        self.sampled_precipitation_times_dict = \
            get_sampled_precipitation_times_in_period(seed=self.seed,
                                                      start_date=self.start_date,
                                                      end_date=self.end_date)

    '''
    # Based the dict of precipitation in a period of time, sample the the fact 
    of whether precipitation occurs or not, 
    from the start_date, ... 
    '''

    def precipitation_interrupt_erection(self):
        random.seed(self.seed)
        """precipitation interrupts the erection every now and then."""
        print("precipitation sampling begins")
        current_date = self.start_date
        day_delta = timedelta(days=1)
        while current_date <= self.end_date:
            current_date_str = current_date.strftime('%Y%m%d')
            print("U: ", current_date_str, ':', "@", self.env.now, end='')
            # if sampled precipitation times is 0, continue working time until next 8 hours.
            if self.sampled_precipitation_times_dict[
                current_date_str.replace(current_date_str[:4], "2012")
            ] == 0:
                # pass current_date_str to the interrupted activity
                self.current_date_str = current_date_str
                # pass the time to simulate work in normal condition
                normal_working_time = self.get_time_to_next_precipitation()
                print(" Project continues for ", normal_working_time, "hs , @",
                      self.env.now)
                yield self.env.timeout(normal_working_time)
            else:  # sampled precipitation times is more than 0, interrupt the current process.
                # pass a random amount time to simulate
                yield self.env.timeout(self.get_time_precipitation_occur_after_erection_start())
                print(" PREC interrupt after", normal_working_time +
                      self.get_time_precipitation_occur_after_erection_start(), " hrs working, @",
                      self.env.now)
                # check all the main processes
                for process in Commons.main_process_list:
                    # if the process is being interrupted, it cannot be interrupted again.
                    if process in Commons.being_interrupted_process_list:
                        print("@", self.env.now,
                              "fails to interrupt, the current process is being interrupted by another "
                              "event")
                    else:
                        #  if a process is currently working
                        if process.is_alive:
                            # print("break a activity")
                            process.interrupt(self)
                            # append the process to Commons.being_interrupted_process_list
                            Commons.being_interrupted_process_list.append(process)
                        # originally for travers all the processes including not alive ones. deprecated
                        else:
                            print("Not Alive")
            # next day
            current_date += day_delta
